﻿Imports System.Net
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Collections.Specialized

Public Class FormMain
    Dim InfoGener As InfoGen
#Region "Win32API"
    ''' <summary>
    ''' 向系统注册热键
    ''' </summary>
    ''' <param name="hwnd">响应热键的窗体句柄</param>
    ''' <param name="id">热键的全局唯一标识符，应介于&amp;HC000和&amp;HFFFF之间</param>
    ''' <param name="fsModifiers">由<code>ControlKey</code>定义的控制键值</param>
    ''' <param name="vk">具体按键值，在<code>System.Windows.Forms.Keys</code>中定义</param>
    ''' <returns>指示绑定热键是否成功，如不为0则成功</returns>
    ''' <remarks></remarks>
    <DllImport("user32.dll", CharSet:=CharSet.Auto, EntryPoint:="RegisterHotKey")>
    Private Shared Function RegisterHotKey(ByVal hwnd As IntPtr, ByVal id As IntPtr,
                                           ByVal fsModifiers As IntPtr, ByVal vk As IntPtr) As IntPtr
    End Function
    ''' <summary>
    ''' 向系统取消注册热键
    ''' </summary>
    ''' <param name="hwnd">注册热键时注册的窗体句柄</param>
    ''' <param name="id">热键的全局唯一标识符，应介于&amp;HC000和&amp;HFFFF之间</param>
    ''' <returns>指示解除注册成功与否</returns>
    ''' <remarks></remarks>
    <DllImport("user32.dll", CharSet:=CharSet.Auto, EntryPoint:="UnregisterHotKey")>
    Private Shared Function UnregisterHotKey(ByVal hwnd As IntPtr, ByVal id As Integer) As Boolean
    End Function
    ''' <summary>
    ''' 表示控制键，可以按位组合以表示同时按下
    ''' </summary>
    ''' <remarks></remarks>
    <Flags()>
    Enum ControlKey As Integer
        None = &H0
        Alt = &H1
        Ctrl = &H2
        Shift = &H4
        Win = &H8
    End Enum
    ''' <summary>
    ''' 注册的热键ID
    ''' </summary>
    ''' <remarks></remarks>
    Dim HotKeyID As Integer
    ''' <summary>
    ''' 指示是否已经注册热键
    ''' </summary>
    ''' <remarks></remarks>
    Dim BindKey As Boolean = False
    ''' <summary>
    ''' 处理窗体消息（已重写）
    ''' </summary>
    ''' <param name="m">窗体接收到的消息</param>
    ''' <remarks></remarks>
    <System.Diagnostics.DebuggerStepThrough()>
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)
        If m.Msg = &H312 Then
            If m.WParam = HotKeyID Then
                PD.BeginInvoke(Nothing, Nothing)
                Debug.WriteLine("KeyPressed")
            End If
        End If
    End Sub
#End Region
#Region "设置"
    ''' <summary>
    ''' 读取用户设置并据此调整控件显示
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ReadSettings()
        With My.Settings
            txtTweet.Text = .TweetMod
            tmrTicker.Enabled = .Enable
            chkTime.Checked = .UpdateByTime
            chkKey.Checked = .UpdateByKey
            chkAlt.Checked = .KeyAlt
            chkCtrl.Checked = .KeyCtrl
            chkShift.Checked = .KeyShift
            chkWin.Checked = .KeyWin
            Dim s As String = [Enum].GetName(GetType(Keys), .KeyMain)
            If Not String.IsNullOrEmpty(s) Then cmbKey.SelectedItem = s
            If .Enable Then
                .Enable = False
                btnEnable_Click(Nothing, Nothing)
            End If
        End With
        '获取程序版本信息
        Try
            If Deployment.Application.ApplicationDeployment.IsNetworkDeployed Then
                LabelVersion.Text = System.Deployment.Application.ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString
            Else
                LabelVersion.Text = My.Application.Info.Version.ToString
            End If
        Catch ex As Exception

        Finally
            If String.IsNullOrEmpty(LabelVersion.Text) Then LabelVersion.Text = My.Application.Info.Version.ToString
        End Try
    End Sub
    ''' <summary>
    ''' 根据用户的选择写入用户设置
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub WriteSettings()
        With My.Settings
            .Interval = CInt(If(nudInterval.Value <> 0, nudInterval.Value, 5)) * 60000
            tmrTicker.Interval = If(My.Settings.Interval = 0, 10000, My.Settings.Interval)
            .TweetMod = txtTweet.Text
            .UpdateByKey = chkKey.Checked
            .UpdateByTime = chkTime.Checked
            .KeyAlt = chkAlt.Checked
            .KeyCtrl = chkCtrl.Checked
            .KeyShift = chkShift.Checked
            .KeyWin = chkWin.Checked
            On Error Resume Next
            Dim k As Keys
            .KeyMain = DirectCast([Enum].Parse(k.GetType, cmbKey.SelectedItem), Integer)
        End With

    End Sub

#End Region

#Region "一般事件处理"


    ''' <summary>
    ''' 处理按下“储存设置”时的动作
    ''' </summary>
    ''' <param name="sender">动作的发起者，一般为Nothing</param>
    ''' <param name="e">事件附加参数，一般留空</param>
    ''' <remarks></remarks>
    Private Sub btnOkay_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOkay.Click
        WriteSettings()
    End Sub

    Sub BindHotKey()
        Dim modifer As ControlKey = ControlKey.None
        If My.Settings.KeyAlt Then modifer = modifer Or ControlKey.Alt
        If My.Settings.KeyCtrl Then modifer = modifer Or ControlKey.Ctrl
        If My.Settings.KeyShift Then modifer = modifer Or ControlKey.Shift
        If My.Settings.KeyWin Then modifer = modifer Or ControlKey.Win
        HotKeyID = Now.Ticks Mod Int32.MaxValue
        If RegisterHotKey(Handle, HotKeyID, modifer, My.Settings.KeyMain) = IntPtr.Zero Then
            Throw New ApplicationException("不能成功绑定热键！")
        End If
        BindKey = True
    End Sub

    Sub StartWork()
        My.Settings.Enable = True
        tmrTicker.Interval = nudInterval.Value * 60000
        If My.Settings.UpdateByTime Then tmrTicker.Enabled = True Else tmrTicker.Enabled = False
        'Win32 API
        'RegistryHotKey
        If My.Settings.UpdateByKey Then
            BindHotKey()

        End If
        btnEnable.Text = "停止工作"
        开始工作ToolStripMenuItem.Text = "停止工作"
    End Sub

    Sub EndWork()
        'UnregistryHotKey
        If BindKey Then
            If Not UnregisterHotKey(Handle, HotKeyID) Then
                Throw New ApplicationException("未能成功解除热键绑定！")
            End If
        End If
        My.Settings.Enable = False
        tmrTicker.Enabled = False
        btnEnable.Text = "开始工作"
        开始工作ToolStripMenuItem.Text = "开始工作"
    End Sub

    Private Sub btnEnable_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEnable.Click, 开始工作ToolStripMenuItem.Click
        Try
            If Not My.Settings.Enable Then
                btnEnable.Text = "正在启动..."
                If (From snd In Senders.GetAvailabilities Where snd.Value = True).Count <> 0 Then
                    StartWork()
                Else
                    If MessageBox.Show("没有可用的发送器，强制启动？", "无发送器", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) = Windows.Forms.DialogResult.OK Then
                        StartWork()
                    Else
                        EndWork()
                    End If
                End If
            Else
                EndWork()
            End If
        Catch ex As WebException
            MessageBox.Show("无法连接到twitter服务器，请检查设置并重试。",
                            "连接错误", MessageBoxButtons.OK)
            EndWork()
        Catch ex As ApplicationException
            MessageBox.Show("发生错误，错误原因为： " + ex.Message,
                            "程序错误", MessageBoxButtons.OK)
            EndWork()
        Catch ex As Exception
            MessageBox.Show("遇到未知错误，错误信息：" +
                            Environment.NewLine +
                            ex.ToString, "未知错误",
                            MessageBoxButtons.OK)
            EndWork()
        End Try
    End Sub

    ''' <summary>
    ''' 指示窗体是否显示（无法侦测被其它程序强制显示的情况）
    ''' </summary>
    ''' <remarks></remarks>
    Dim isShow As Boolean = True
    Private Sub Notify_MouseDoubleClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Notify.MouseDoubleClick, 设置ToolStripMenuItem.Click
        If isShow Then
            Me.Hide()
            isShow = False
        Else
            Me.Show()
            isShow = True
        End If
    End Sub

    Private Sub FormMain_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        PluginShutDown()
    End Sub

    Private Sub FormMain_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If BindKey Then
            UnregisterHotKey(Handle, HotKeyID)
        End If
        If InfoGener IsNot Nothing Then
            InfoGener.Dispose()
        End If
    End Sub

    Private Sub FormMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        '初始化cmbKey
        cmbKey.BeginUpdate()
        cmbKey.Items.Clear()
        cmbKey.Items.AddRange([Enum].GetNames(GetType(Keys)))
        cmbKey.EndUpdate()
        ReadSettings()
        PluginStartUp()
    End Sub

    Private Sub btnApply_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnApply.Click
        Me.Hide()
        isShow = False
    End Sub

    Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
        With My.Settings
            txtTweet.Text = .TweetMod
            nudInterval.Value = .Interval \ 60000
        End With
    End Sub

    Dim PD As Action = AddressOf PostData
    Private Sub tmrTicker_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrTicker.Tick, 现在更新ToolStripMenuItem.Click
        PD.BeginInvoke(Nothing, Nothing)
    End Sub
    ''' <summary>
    ''' 发送当前歌曲信息
    ''' </summary>
    ''' <remarks></remarks>
    Sub PostData()
        Dim s As String = GetDatatoPost()
        If String.IsNullOrEmpty(s) Then Return
        Senders.Update(s)
    End Sub
    ''' <summary>
    ''' 指示是否是第一次显示窗体（打开程序时）
    ''' </summary>
    ''' <remarks></remarks>
    Dim FirstShown As Boolean = True
    Private Sub FormMain_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        If FirstShown Then
            If My.Application.CommandLineArgs.Count > 0 Then
                If My.Application.CommandLineArgs(0).Equals("hidden") Then
                    Me.Hide()
                    isShow = False
                End If
            End If
            FirstShown = False
        End If
    End Sub

    Private Sub 退出ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 退出ToolStripMenuItem.Click
        Me.Close()
    End Sub

#End Region

#Region "信息获取"
    ''' <summary>
    ''' 获取要发送的tweet全文
    ''' </summary>
    ''' 
    ''' <returns>要发送的tweet全文</returns>
    ''' <remarks></remarks>
    Public Overridable Function GetDatatoPost() As String
        If InfoGener.GetAvailablePlayer.Count = 0 Then Return Nothing
        Dim infos = InfoGener.GetInfomation
        Dim result As String =
            reg.Replace(txtTweet.Text,
                        Function(ma)
                            Dim s As String = ma.Groups("tag").Value
                            If s.Equals("realtimecomment") Then
                                Return "<realtimecomment>"
                            Else
                                Return If(infos(s), String.Empty)
                            End If
                        End Function)
        If result.Contains("<realtimecomment>") Then
            DialogComment.txtComment.Text = ""
            DialogComment.ShowDialog(140 - result.Replace("<realtimecomment>", "").Length)
            result = result.Replace("<realtimecomment>", DialogComment.txtComment.Text)
        End If
        Return result
    End Function

    ReadOnly reg As New Regex("<(?<tag>[a-zA-Z]+)>", RegexOptions.IgnoreCase And RegexOptions.IgnoreCase)

    Public Function GetDatatoPost(ByVal Players As List(Of String)) As String
        Dim infos = InfoGener.GetInfomation(Players)
        Dim result As String =
            reg.Replace(txtTweet.Text,
                        Function(ma)
                            Dim s As String = ma.Groups("tag").Value
                            If s.Equals("realtimecomment") Then
                                Return "<realtimecomment>"
                            Else
                                Return If(infos(s), String.Empty)
                            End If
                        End Function)
        If result.Contains("<realtimecomment>") Then
            DialogComment.txtComment.Text = ""
            DialogComment.ShowDialog(140 - result.Replace("<realtimecomment>", "").Length)
            result = result.Replace("<realtimecomment>", DialogComment.txtComment.Text)
        End If
        Return result
    End Function

#End Region

#Region "Plugins"

    Dim SecureSettings As Settings
    Dim Senders As InfoSenders

    Private Sub PluginStartUp()
        Debug.WriteLine("StartUp Plugins")
        Senders = New InfoSenders(SecureSettings.SenderAddinPaths)
        UpdateSenderAddinPath()
    End Sub

    Private Sub PluginShutDown()
        Senders.Dispose()
        Senders = Nothing
    End Sub

    Private Sub RenewSenderPlugins()
        If Senders IsNot Nothing Then Senders.Dispose()
        Senders = New InfoSenders(My.Settings.SenderSearchPath.OfType(Of String))
        ChangeEnableState()
    End Sub

    Private Sub ChangeEnableState()
        Dim es = Senders.EnableSenders
        For Each s In (From snd In Senders.SenderNames Where My.Settings.SenderSearchPath.Contains(snd))
            If es.ContainsKey(s) Then
                es(s) = True
            Else
                es.Add(s, True)
            End If
        Next
    End Sub

    Private Sub UpdateSenderAddinPath()
        RenewSenderPlugins()
        SenderAddinSearchList.BeginUpdate()
        SenderAddinSearchList.Items.Clear()
        SenderAddinSearchList.Items.AddRange(My.Settings.SenderSearchPath.OfType(Of String).ToArray)
        SenderAddinSearchList.EndUpdate()
        UpdateSenderAddins()
    End Sub

    Private Sub UpdateSenderAddins()
        SenderAddinList.BeginUpdate()
        SenderAddinList.Items.Clear()
        SenderAddinList.Items.AddRange(Senders.SenderNames.ToArray)
        SenderAddinList.EndUpdate()
        Dim es = Senders.EnableSenders
        For i As Integer = 0 To SenderAddinList.Items.Count - 1
            If es.ContainsKey(SenderAddinList.Items(i).ToString) AndAlso es(SenderAddinList.Items(i).ToString) Then
                SenderAddinList.SetItemChecked(i, True)
            Else
                SenderAddinList.SetItemChecked(i, False)
            End If
        Next
    End Sub

    Dim d As New Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialog("选择插件目录")
    Private Sub AddSenderAddinPathButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddSenderAddinPathButton.Click
        d.IsFolderPicker = True
        d.Multiselect = False
        d.NavigateToShortcut = True
        d.Title = "选择插件目录"
        d.EnsurePathExists = True
        If d.ShowDialog = Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogResult.Ok Then
            If My.Settings.SenderSearchPath Is Nothing Then My.Settings.SenderSearchPath = New StringCollection()
            My.Settings.SenderSearchPath.Add(d.FileName)
            UpdateSenderAddinPath()
        End If
    End Sub

    Private Sub RemoveSenderAddinPathButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RemoveSenderAddinPathButton.Click
        Dim l = From snd In My.Settings.SenderSearchPath Join item In SenderAddinSearchList.SelectedItems On item.ToString Equals snd Select snd

        For Each path In l.ToArray
            My.Settings.SenderSearchPath.Remove(path)
        Next
        UpdateSenderAddinPath()
    End Sub

    Private Sub ReloadSenderListButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReloadSenderListButton.Click
        RenewSenderPlugins()
        UpdateSenderAddins()
    End Sub

    Private Sub OpenSenderSettingButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenSenderSettingButton.Click
        If SenderAddinList.SelectedItem Is Nothing Then Return
        Senders.ShowSetting(SenderAddinList.SelectedItem.ToString)
    End Sub

    Private Sub CheckSenderAvailableButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckSenderAvailableButton.Click
        Dim sb As New StringBuilder
        sb.AppendLine("State Infomation:")
        sb.AppendLine("------------------------")
        For Each kp In Senders.GetAvailabilities
            sb.AppendFormat("{0}:{1}" + Environment.NewLine, kp.Key, If(kp.Value, "OK", "Down"))
        Next
        MessageBox.Show(sb.ToString, "State")
    End Sub
#End Region

    Private InfoGenState As New Dictionary(Of String, Tuple(Of Boolean, Integer))

    Private Sub InitInfoGen()
        If InfoGener IsNot Nothing Then InfoGener.Dispose()
        InfoGener = New InfoGen(My.Settings.InfoGenSearchPath.OfType(Of String))
        Dim sp = InfoGener.GetSupportedPlayers
        If My.Settings.EnabledInfoGen Is Nothing Then My.Settings.EnabledInfoGen = New StringCollection
        Dim DEIG = (From p In sp).ToDictionary(Function(k) k,
                                               Function(v) Tuple.Create(Of Boolean, Integer)(
                                                                 My.Settings.EnabledInfoGen.Contains(v),
                                                                 If(My.Settings.EnabledInfoGen.Contains(v),
                                                                    My.Settings.EnabledInfoGen.IndexOf(v),
                                                                    Integer.MaxValue)))
        InfoGenState = DEIG
        InfoGenList.BeginUpdate()
        InfoGenList.Items.Clear()
        For Each snd In (From ig In DEIG Order By ig.Value.Item2 Ascending, ig.Key Ascending Select name = ig.Key, enable = ig.Value.Item1)
            InfoGenList.Items.Add(snd.name, snd.enable)
        Next
        InfoGenList.EndUpdate()
    End Sub

    Private Sub CheckGenAvailableButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckGenAvailableButton.Click
        Dim sb As New StringBuilder
        sb.AppendLine("State Infomation:")
        sb.AppendLine("------------------------")
        Dim a = InfoGener.GetAvailablePlayer
        Dim s = InfoGener.GetSupportedPlayers
        For Each p In s
            sb.AppendFormat("{0}:{1}" + Environment.NewLine, p, If(a.Contains(p), "OK", "Down"))
        Next
        MessageBox.Show(sb.ToString, "State")
    End Sub

    Private Sub AddGenAddinSearchPathButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddGenAddinSearchPathButton.Click
        d.IsFolderPicker = True
        d.Multiselect = False
        d.NavigateToShortcut = True
        d.Title = "选择插件目录"
        d.EnsurePathExists = True
        If d.ShowDialog = Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogResult.Ok Then
            Dim f = d.FileName
            If My.Settings.InfoGenSearchPath Is Nothing Then
                My.Settings.InfoGenSearchPath = New StringCollection()
            Else
                If My.Settings.InfoGenSearchPath.Contains(f) Then Return
            End If
            My.Settings.InfoGenSearchPath.Add(f)
            InfoGenSearchList.Items.Add(f)
            UpdateGenAddinPath()
            RenewInfoGenPlugins()
        End If
    End Sub

    Private Sub UpdateGenAddinPath()
        InfoGenSearchList.BeginUpdate()
        InfoGenSearchList.Items.Clear()
        For Each path In My.Settings.InfoGenSearchPath
            InfoGenSearchList.Items.Add(path)
        Next
        InfoGenSearchList.EndUpdate()
    End Sub

    Private Sub ReloadGenButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReloadGenButton.Click
        RenewInfoGenPlugins()
        UpdateInfoGenAddins()
    End Sub

    Private Sub RenewInfoGenPlugins()
        If InfoGener IsNot Nothing Then InfoGener.Dispose()
        InitInfoGen()
    End Sub

    Private Sub UpdateInfoGenAddins()
        InfoGenList.BeginUpdate()
        InfoGenList.Items.Clear()
        InfoGenList.Items.AddRange(InfoGenState.Keys.OrderBy(Function(k) InfoGenState(k).Item2).ToArray)
        InfoGenList.EndUpdate()
        If InfoGenState.Count = 0 Then Return
        For i As Integer = 0 To SenderAddinList.Items.Count - 1
            InfoGenList.SetItemChecked(i, InfoGenState(InfoGenList.Items(i).ToString).Item1)
        Next
    End Sub

    Private Sub RemoveGenAddinSearchPathButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RemoveGenAddinSearchPathButton.Click
        For Each path In InfoGenSearchList.SelectedItems
            If My.Settings.InfoGenSearchPath.Contains(path.ToString) Then
                My.Settings.InfoGenSearchPath.Remove(path.ToString)
            End If
        Next
        InitInfoGen()
    End Sub

    Private Sub IGMoveUpButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles IGMoveUpButton.Click
        If InfoGenList.SelectedItem IsNot Nothing Then

        End If
    End Sub
End Class
